#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/err.h>
#include <linux/compiler.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <asm/arch/regs-gpio.h>
#include <linux/timer.h>
#include <linux/wait.h>
#include <linux/input.h>
#include <linux/bitops.h>


static void keys_timer_timeout(unsigned long data);

typedef unsigned int    uint32;
struct pindesc
{
    uint32          pin;
    uint32          id;
};
struct keyinfo
{
    char            *name;
    uint32          id;
    uint32          pin;
    uint32          irqno;
    int             flags;
    uint32          key;
    uint32          key_val;        //0: 按键按下；1：按键松开
    
};

static struct keyinfo   keysinfo[] =
{
    [0] = {.name  = "eint0"  , .id = 0, .pin = S3C2410_GPF0,  .irqno  = IRQ_EINT0  , .flags = -1, .key = KEY_L,          .key_val = 1},
    [1] = {.name  = "eint2"  , .id = 1,  .pin = S3C2410_GPF2,  .irqno  = IRQ_EINT2  , .flags = -1, .key = KEY_S,          .key_val = 1},
    [2] = {.name  = "eint11" , .id = 2,  .pin = S3C2410_GPG3,  .irqno  = IRQ_EINT11 , .flags = -1, .key = KEY_ENTER,      .key_val = 1},
    [3] = {.name  = "eint19" , .id = 3,  .pin = S3C2410_GPG11, .irqno  = IRQ_EINT19 , .flags = -1, .key = KEY_LEFTSHIFT,  .key_val = 1}
};

struct mydev
{
    struct input_dev        *input_device;
    struct timer_list       keys_timer;
    int                     flags;

};

static struct mydev keys_dev = {
    .input_device   = NULL,
	.keys_timer     = TIMER_INITIALIZER(keys_timer_timeout, 10, keysinfo),
	.flags          = -1
};

static int   g_key_index = -1;

static void keys_timer_timeout(unsigned long data)
{ 
    if(g_key_index == -1) return;
    
    struct keyinfo *ksinfo = (struct keyinfo *)data;
    uint32 key_idx = g_key_index;
    uint32 pinval = s3c2410_gpio_getpin(ksinfo[key_idx].pin);
    ksinfo[key_idx].key_val = !pinval ? 0 : 1;
    if(!pinval) //按下
        printk(KERN_DEBUG "The key[%s] is pressed\n", ksinfo[key_idx].name);
    else
        printk(KERN_DEBUG "The key[%s] is released\n", ksinfo[key_idx].name);
    input_event(keys_dev.input_device, EV_KEY, ksinfo[key_idx].key, ksinfo[key_idx].key_val);
    //input_sync(keys_dev.input_device);
}

static irqreturn_t keys_irq_handler(int irqno, void *dev_id)
{
    irqreturn_t irq_ret = IRQ_HANDLED;
    struct keyinfo *kifno =  (struct keyinfo *)dev_id;
    g_key_index = kifno->id;
    /* jiffies: 
     *   Linux内核使用全局变量jiffies记录系统自从启动以来的滴答数。在系统启动的时初始化jiffies为0，
     *   在每个时钟中断处理例程中该值会加1。假如HZ=1000，每隔1ms系统会发生一次时钟中断，
     *   也就是说每隔1ms jiffies的值会加1，也就是说1s内jiffies的值也是HZ，所以系统启动的时间就是： jiffies/HZ
     * mod_timer: 延长定时器的超时值（在10ms之内如果按键产生多次中断，那么也只处理一次）
     * 
     */
    mod_timer(&keys_dev.keys_timer, jiffies+HZ/100);
    
    return IRQ_RETVAL(irq_ret);
}


static int __init keydrv_init(void)
{
    int i;
    //[0]. 申请一个struct input_dev内存空间
    keys_dev.input_device   =   input_allocate_device();
    if(!keys_dev.input_device)
    {
        printk(KERN_DEBUG "Alloc the memory for key dev failed!\n");
        goto ERR;
    }
    //[1]. 设置key输入设备驱动可以产生按键类事件
    set_bit(EV_KEY, keys_dev.input_device->evbit);
    //[2]. 设置key输入设备有哪些可以产生按键类事件的按键
    set_bit(KEY_L , keys_dev.input_device->keybit);
    set_bit(KEY_S , keys_dev.input_device->keybit);
    set_bit(KEY_ENTER , keys_dev.input_device->keybit);
    set_bit(KEY_LEFTSHIFT , keys_dev.input_device->keybit);
    //[3]. 将按键输入设备注册到输入核心子系统驱动中
    if(input_register_device(keys_dev.input_device))
    {
        printk(KERN_DEBUG "Register key input device failed!\n");
        goto ERR;
    }
    //[4] 注册按键中断处理程序
    for(i = 0; i < 4; ++i)
    {
        if(request_irq(keysinfo[i].irqno, keys_irq_handler,IRQT_BOTHEDGE, keysinfo[i].name, &keysinfo[i]) < 0)
           printk(KERN_WARNING "Register irq for [%s] failed!\n", keysinfo[i].name);
        else
            keysinfo[i].flags = 0;
    }

    //[3] 初始化一个定时器，防止按键抖动
    init_timer(&keys_dev.keys_timer);
    //[4] 启动定时器
    add_timer(&keys_dev.keys_timer);
    
    return 0;
ERR:
    printk(KERN_ERR "The driver 'keys_drv' register failed\n");
    if(keys_dev.input_device)
        input_free_device(keys_dev.input_device);
	keys_dev.flags = -1;
	return -1;
}
static void __exit keydrv_exit(void)
{
    int i;
    printk(KERN_DEBUG "The driver 'keys_drv' is removed!\n");
	if(!keys_dev.flags)
	{

        for(i = 0; i < 4; ++i)
        {
            if(!keysinfo[i].flags)
                free_irq(keysinfo[i].irqno, &keysinfo[i]);
            keysinfo[i].flags = -1;
        }
        del_timer(&keys_dev.keys_timer);
        input_unregister_device(keys_dev.input_device);
        input_free_device(keys_dev.input_device);
	}
}



module_init(keydrv_init);
module_exit(keydrv_exit);


MODULE_LICENSE("GPL");

